﻿//OrderLogGenerator.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Algo.Testing
{
	using System;

	using Ecng.Collections;
	using Ecng.Common;

	using StockSharp.BusinessEntities;

	/// <summary>
	/// Генератор лога заявок случайным методом.
	/// </summary>
	public class OrderLogGenerator : MarketDataGenerator<OrderLogItem>
	{
		private decimal _lastOrderPrice;
		private readonly SynchronizedQueue<Order> _activeOrders = new SynchronizedQueue<Order>(); 

		/// <summary>
		/// Создать <see cref="OrderLogGenerator"/>.
		/// </summary>
		/// <param name="security">Инструмент, для которого необходимо генерировать данные.</param>
		/// <param name="startPrice">Цена, от которой начинается расчёт нормального распределения.</param>
		public OrderLogGenerator(Security security, decimal startPrice)
			: this(security, startPrice, new RandomWalkTradeGenerator(security, startPrice))
		{
		}

		/// <summary>
		/// Создать <see cref="OrderLogGenerator"/>.
		/// </summary>
		/// <param name="security">Инструмент, для которого необходимо генерировать данные.</param>
		/// <param name="startPrice">Цена, от которой начинается расчёт нормального распределения.</param>
		/// <param name="tradeGenerator">Генератор тиковых сделок случайным методом.</param>
		public OrderLogGenerator(Security security, decimal startPrice, TradeGenerator tradeGenerator)
			: base(security)
		{
			if (tradeGenerator == null)
				throw new ArgumentNullException("tradeGenerator");

			_lastOrderPrice = startPrice;

			TradeGenerator = tradeGenerator;
			IdGenerator = new IdGenerator();
		}

		/// <summary>
		/// Генератор тиковых сделок случайным методом.
		/// </summary>
		public TradeGenerator TradeGenerator { get; private set; }

		private IdGenerator _idGenerator;

		/// <summary>
		/// Генератор номера заявки <see cref="Order.Id"/>.
		/// </summary>
		public IdGenerator IdGenerator
		{
			get { return _idGenerator; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				_idGenerator = value;
			}
		}

		/// <summary>
		/// Инициализировать состояние генератора.
		/// </summary>
		public override void Init()
		{
			TradeGenerator.Init();
			base.Init();
		}

		/// <summary>
		/// Сгенерировать случайную операцию.
		/// </summary>
		/// <param name="time">Время операции.</param>
		/// <returns>Случайная операция.</returns>
		public virtual OrderLogItem Generate(DateTime time)
		{
			// TODO более реалистичную генерацию, так как сейчас объемы, цены и сделки c потолка

			var action = RandomGen.GetInt(0, 5);

			var isNew = action < 3 || _activeOrders.IsEmpty();

			var quote = new OrderLogItem();

			if (isNew)
			{
				_lastOrderPrice += RandomGen.GetInt(-MaxPriceStepCount, MaxPriceStepCount) * Security.MinStepSize;

				if (_lastOrderPrice <= 0)
					_lastOrderPrice = Security.MinStepSize;

				quote.Order = new Order
				{
					Id = IdGenerator.Next,
					Security = Security,
					Time = time,
					State = OrderStates.Active,
					Volume = Volumes.Next(),
					Direction = RandomGen.GetEnum<OrderDirections>(),
					Price = _lastOrderPrice,
				};

				var activeOrder = quote.Order.Clone();

				activeOrder.Id = quote.Order.Id;
				activeOrder.State = OrderStates.Active;
				activeOrder.Time = quote.Order.Time;

				_activeOrders.Enqueue(activeOrder);
			}
			else
			{
				var activeOrder = _activeOrders.Peek();

				quote.Order = activeOrder.Clone();
				quote.Order.Id = activeOrder.Id;
				quote.Order.Time = time;

				var isMatched = action == 5;

				if (isMatched)
				{
					quote.Order.Volume = RandomGen.GetInt(1, (int)activeOrder.Volume);

					quote.Trade = TradeGenerator.Generate(time);
					quote.Trade.Volume = quote.Order.Volume;

					if (quote.Order.Direction == OrderDirections.Buy && quote.Trade.Price > quote.Order.Price)
						quote.Trade.Price = quote.Order.Price;
					else if (quote.Order.Direction == OrderDirections.Sell && quote.Trade.Price < quote.Order.Price)
						quote.Trade.Price = quote.Order.Price;

					activeOrder.Volume -= quote.Order.Volume;

					if (activeOrder.Volume == 0)
					{
						quote.Order.State = OrderStates.Done;
						_activeOrders.Dequeue();
					}
					else
						quote.Order.State = OrderStates.Active;
				}
				else
				{
					quote.Order.State = OrderStates.Done;
					_activeOrders.Dequeue();
				}
			}

			Generate(quote, time);

			return quote;
		}

		/// <summary>
		/// Создать копию генератора.
		/// </summary>
		/// <returns>Копия.</returns>
		public override MarketDataGenerator<OrderLogItem> Clone()
		{
			return new OrderLogGenerator(Security, _lastOrderPrice, (TradeGenerator)TradeGenerator.Clone())
			{
				IdGenerator = IdGenerator
			};
		}
	}
}